using System;
using System.Collections.Generic;
using System.Text;

namespace RapidHDL
{
    public class MemoryBlock
    {
        int iOpenRequests=0;
        bool bInvalidated = false;
        string sLastError = "";

        public Dictionary<int,MemoryEntry> MemoryList = new Dictionary<int,MemoryEntry>();

        public int OpenRequests
        {
            get
            {
                return iOpenRequests;
            }
        }

        public void Reset()
        {
            MemoryList.Clear();
            iOpenRequests = 0;
            bInvalidated = false;
            sLastError = "";
        }

        public void RequestMemory(int piAddress)
        {
            iOpenRequests++;
            MemoryEntry oMemoryEntry = new MemoryEntry(piAddress);
            MemoryList.Add(piAddress, oMemoryEntry);
        }

        public uint ReturnMemory(int piAddress)
        {
            // may be invalidated
            // may be open request
            MemoryEntry oMemoryEntry = MemoryList[piAddress];
            return oMemoryEntry.Data;
        }

        public void WriteMemory(int piAddress, uint piData)
        {
            iOpenRequests++;
            MemoryEntry oMemoryEntry = new MemoryEntry(piAddress, piData);
            MemoryList.Add(piAddress, oMemoryEntry);
        }

        public void ToPacketBlock(ref RequestSlots poSlots, ref PacketBlock poPacketBlock)
        {
            poPacketBlock.Reset();
            uint iRoute = 0;
            uint iData = 0;
            uint iSlot = 0;
            uint iAddress = 0;
            uint iHeader = 0;
            foreach (MemoryEntry oMemoryEntry in MemoryList.Values)
            {
                iSlot = (((uint)poSlots.RequestSlot(oMemoryEntry.Address) << 24) & 0x3F000000);
                iAddress = (uint)oMemoryEntry.Address & (uint)0x00FFFFFF;
                if (oMemoryEntry.WriteRequest)
                {
                    iHeader = (uint)0xC0000000;
                }
                else
                {
                    iHeader = (uint)0x40000000;
                }
                iRoute = iHeader | iSlot | iAddress;
                iData = oMemoryEntry.Data;
                
                poPacketBlock.AddPacket(iRoute, iData);
            }
        }

        public void Retry()
        {
            foreach (MemoryEntry oMemoryEntry in MemoryList.Values)
            {
                oMemoryEntry.IsReady = false;
            }
            iOpenRequests = MemoryList.Count;
            bInvalidated = false;
            sLastError = "";
        }

        public void CheckForMemoryUpdates(ref RequestSlots poSlots, ref int [] paUpdatedSlots, int piUpdateCount)
        {
            if (iOpenRequests <= 0)
                return;

            for (int iIdx = 0; iIdx < piUpdateCount; iIdx++)
            {
                int iSlot = paUpdatedSlots[iIdx];
                int iAddress = poSlots.Slots[iSlot].ID;
                if (MemoryList.ContainsKey(iAddress))
                {
                    MemoryEntry oMemoryEntry = MemoryList[iAddress];
                    if (oMemoryEntry.IsReady)
                    {
                        bInvalidated = true;
                        sLastError = "Data Received Twice for " + iAddress.ToString();
                        return;
                    }
                    if (oMemoryEntry.WriteRequest)
                    {
                        if (oMemoryEntry.Data != (uint)poSlots.Slots[iSlot].Data)
                        {
                            //bInvalidated = true;
                            sLastError = "Invalid Data Written for " + iAddress.ToString();
                            return;
                        }
                    }
                    else
                    {
                        oMemoryEntry.Data = (uint)poSlots.Slots[iSlot].Data;
                        oMemoryEntry.IsReady = true;                        
                    }
                    iOpenRequests--;
                    oMemoryEntry.IsReady = true;                    
                }
            }
        }

        public bool IsReady
        {
            get { return (iOpenRequests <= 0); }
        }

        public string DebugDisplay()
        {
            string sResult = "";
            foreach (MemoryEntry oMemoryEntry in MemoryList.Values)
            {
                sResult += oMemoryEntry.Address.ToString() + " = " + oMemoryEntry.Data.ToString();
                if (!oMemoryEntry.IsReady)
                    sResult += " -";
                sResult += "\n";
            }
            return sResult;
        }

        public bool Invalidated
        {
            get { return bInvalidated; }
        }
    }
}
